/* Copyright (c) 2009, 2010, 2011 Damian Kmiecik <d0zoenator@gmail.com>
   All rights reserved.

   Redistribution and use in source and binary forms, with or without
   modification, are permitted provided that the following conditions are met:

   * Redistributions of source code must retain the above copyright
     notice, this list of conditions and the following disclaimer.
   * Redistributions in binary form must reproduce the above copyright
     notice, this list of conditions and the following disclaimer in
     the documentation and/or other materials provided with the
     distribution.
   * Neither the name of the copyright holders nor the names of
     contributors may be used to endorse or promote products derived
     from this software without specific prior written permission.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  POSSIBILITY OF SUCH DAMAGE. */

#ifndef ETHERNET_H_
#define ETHERNET_H_

#include "main.h"

/** @defgroup ETHERNET Ethernet routines; ARP and ICMP protocols
	@author Damian Kmiecik
	@bug any knowns

	Ethernet functions implementation contains ARP, ICMP, IP.
	This code enable to communicate via ethernet link to others host.

	@par Simple implementation contains function:

	- ARP table of finite size. This feature holds MAC addresses of
	last-seen hosts. Every record in ARP table has own life-time.
	After timeout the record will be deleted and free space can be
	used for saved another MAC address.

	- ICMP receiving. This code can receiving an ICMP request and respond to.
	ICMP can contains data. This implementation has fixed data size of 32B. Any
	larger packets will be truncated.

	- ICMP sending. Optionally, we can ping other devices in network. Same
	as before the data size is of fixed length of 32B.

	- IP protocol. The simplest mode is used. Code compute checksum for packet.

	- Simple routing. Stack check that requested IP is from local network or behind
	the gateway. Packets out of the local network will be bypassed to the gateway.

	@par Notes

	- Software checksum computing is very slow option. ENC28J60 has hardware DMA
	controller with build-in checksum computing unit. Though, is not yet implemented.
*/

/** @defgroup UDP UDP sending and receiving
	@author Damian Kmiecik
	@bug any knowns

	UDP packet receiving and sending.
	Implements UDP without computing of checksum.
*/

/** @defgroup TCP TCP stack + event-oriented user application API
	@author Damian Kmiecik
	@bug any knowns

	TCP implementation with event-based Application mode.
*/

#if defined(__AVR_ATmega32__)
	#define MAX_ARP_ENTRY			15
	#define MAX_TCP_ENTRY			30
	#define MAX_UDP_ENTRY			5
	#define MAX_APP_ENTRY			5
	#define TCP_MAX_TIME			30
	#define TCP_REXMIT_TIME			2
	#define TCP_MAX_ERROR_CNT		6
	#define MAX_ARP_ENTRY_TIME		50
	#define ETHERNET_SENDING_PING	0
	#define MTU_SIZE 				600
#endif

/** @def ARP_DEBUG
	@ingroup debug
	Defines the standard method of debugging system.
	If you comment this, there are no debugging messages will be presented.
 */
//#define ARP_DEBUG	DEBUG

/** @def TCP_DEBUG
	@ingroup debug
	Defines the standard method of debugging system.
	If you comment this, there are no debugging messages will be presented.
 */
//#define TCP_DEBUG	DEBUG

/** @def DEBUG_ARPTCP
	@ingroup debug
	Additional debugging of ARP_TABLE or TCP_TABLE. Content will be
	presented on standard output.
	If you comment this, there are no debugging messages will be presented.
 */
//#define DEBUG_ARPTCP

/** @typdef ip_t
	@ingroup ETHERNET
	Preserve IP address.
*/
typedef uint32_t ip_t;

/** @typdef port_t
	@ingroup ETHERNET
	Preserve PORT number.
*/
typedef uint16_t port_t;

/** @struct ETH_ETHERNET_HEADER
	@ingroup ETHERNET
	@ref "http://standards.ieee.org/getieee802/802.3.html"
	@brief Standard ethernet frame header.
 */
struct ETH_ETHERNET_HEADER {
	/** Destination address [MAC] */
	uint8_t destMac[6];
	/** Source address [MAC] */
	uint8_t sourceMac[6];
	/** Type/Length, if bigger then 1500, is a packet type */
	uint16_t type;
};

/** @struct ETH_ARP_HEADER
	@ingroup ETHERNET
	@ref "http://tools.ietf.org/html/rfc826"
	@brief Standard arp frame header.
 */
struct ETH_ARP_HEADER {
	/** Hardware type */
	uint16_t hwType;
	/** Protocol type */
	uint16_t prType;
	/** Hardware size */
	uint8_t hwLen;
	/** Protocol size */
	uint8_t prLen;
	/** Opcode */
	uint16_t opcode;
	/** Sender MAC address */
	uint8_t senderMac[6];
	/** Sender IP address */
	ip_t senderIp;
	/** Target MAC address */
	uint8_t targetMac[6];
	/** Target IP address */
	ip_t targetIp;
};

/** @struct ETH_IP_HEADER
	@ingroup ETHERNET
	@ref "http://tools.ietf.org/html/rfc791"
	@brief Standard ip frame header.
 */
struct ETH_IP_HEADER {
	/** Version */
	uint8_t version;
	/** Header length */
	uint8_t headerLen;
	/** Total length */
	uint16_t totalLen;
	/** Identification **/
	uint16_t id;
	/** Fragment offset */
	uint16_t offset;
	/** Time to live */
	uint8_t ttl;
	/** Protocol */
	uint8_t protocol;
	/** Checksum */
	uint16_t checksum;
	/** Source IP */
	ip_t sourceIp;
	/** Destination IP */
	ip_t destIp;
};

/** @struct ETH_ICMP_HEADER
	@ingroup ETHERNET
	@ref "ftp://ftp.rfc-editor.org/in-notes/rfc792.txt"
	@brief Standard icmp frame header.
 */
struct ETH_ICMP_HEADER {
	/** ICMP type */
	uint8_t type;
	/** Code */
	uint8_t code;
	/** Checksum */
	uint16_t checksum;
	/** Identifier */
	uint16_t id;
	/** Sequence number */
	uint16_t seqNum;
};

/** @struct ETH_UDP_HEADER
	@ingroup UDP
	@ref "ftp://ftp.rfc-editor.org/in-notes/rfc768.txt"
	@brief Standard udp frame header.
 */
struct ETH_UDP_HEADER {
	/** Source port */
	port_t sourcePort;
	/** Destination port */
	port_t destPort;
	/** Length */
	uint16_t length;
	/** Checksum */
	uint16_t checksum;
};

/** @struct ETH_TCP_HEADER
	@ingroup TCP
	@ref "http://tools.ietf.org/html/rfc793"
	@brief Standard tcp frame header.
 */
struct ETH_TCP_HEADER {
	/** Source port */
	port_t sourcePort;
	/** Destination port */
	port_t destPort;
	/** Sequence number */
	uint32_t seqNum;
	/** Acknowledgment number */
	uint32_t ackNum;
	/** Data offset */
	uint8_t offset;
	/** Flags */
	uint8_t flags;
	/** Window */
	uint16_t window;
	/** Checksum */
	uint16_t checksum;
	/** Urgent pointer */
	uint16_t urgentPtr;
};

/** @struct TCP_PORT_ITEM
	@ingroup TCP
	@brief Structure to holds opened ports number for TCP.
	Based on this, stack may discard connection or accept.
	Every opened port has own "application" for receiving and process data.
 */
struct TCP_PORT_ITEM {
	/** Port number */
	port_t port;
};

/** @struct UDP_PORT_ITEM
	@ingroup UDP
	@brief Structure to holds opened ports number for UDP.
	Based on this, stack may discard data or accept.
	Every opened port has own "application" for receiving and process data.
 */
struct UDP_PORT_ITEM {
	/** Port number */
	port_t port;
};

/** @struct ARP_TABLE
	@ingroup ETHERNET
	@brief ARP table item.
	Preserve MAC addresses of known hosts.
 */
struct ARP_TABLE {
	/** MAC address */
	uint8_t arp_t_mac[6];
	/** IP address */
	ip_t arp_t_ip;
	/** life-time */
	uint8_t arp_t_time;
};

/** @struct TCP_TABLE
	@ingroup TCP
	@brief TCP table item.
	Preserve state of each TCP connection.
 */
struct TCP_TABLE {
	/** client IP  */
	ip_t ip;
	/** source port */
	port_t src_port;
	/** destination port */
	port_t dest_port;
	/** ack counter */
	uint32_t ack_counter;
	/** sequence conter */
	uint32_t seq_counter;
	/** app status - user data
		user can preserve application data in this array
		app-status can be modified with any restrictions
	*/
	uint8_t app_status[8];
	/** flags */
	uint8_t flags :5;
	/** error counter */
	uint8_t err :3;
	/** life-time
		Can be set to \a TCP_TIME_OFF for countdown disable
	*/
	uint8_t time :6;
	/** First ack flag */
	uint8_t first_ack :1;
	/** Sending flag, this flag is set when \a TCPsend
		command has been executed.
	*/
	uint8_t send :1;
};

/** @struct TCP_STATUS
	@ingroup TCP
	@brief TCP event type.
	This struct can be used by user-application
	to identify event type.
*/
struct TCP_STATUS {
	/** event status */
	uint8_t status;
};

/** @struct ETHERNET
	@ingroup ETHERNET
	@brief ICMP sending data.
	Can be used to determine that pong received.
*/
struct ETHERNET {
	#if ETHERNET_SENDING_PING
		uint32_t ping_ip;
		uint8_t ping;
	#endif
};

#define ETH_TCP_HEADER_LEN		20
#define ETH_UDP_HEADER_LEN		8
#define ETH_ICMP_HEADER_LEN		8
#define ETH_IP_HEADER_LEN		20
#define ETH_ARP_HEADER_LEN		18
#define ETH_ETHERNET_HEADER_LEN	14

/** @def TCP_FIN_FLAG
	@ingroup TCP
	TCP header flag. FIN flag.
	Can be used to ETH_TCP_HEADER->flags
*/
#define TCP_FIN_FLAG	0x01

/** @def TCP_SYN_FLAG
	@ingroup TCP
	TCP header flag. SYN flag.
	Can be used to ETH_TCP_HEADER->flags
*/
#define TCP_SYN_FLAG	0x02

/** @def TCP_RST_FLAG
	@ingroup TCP
	TCP header flag. RST flag.
	Can be used to ETH_TCP_HEADER->flags
*/
#define TCP_RST_FLAG	0x04

/** @def TCP_PSH_FLAG
	@ingroup TCP
	TCP header flag. PSH flag.
	Can be used to ETH_TCP_HEADER->flags
*/
#define TCP_PSH_FLAG	0x08

/** @def TCP_ACK_FLAG
	@ingroup TCP
	TCP header flag. ACK flag.
	Can be used to ETH_TCP_HEADER->flags
*/
#define TCP_ACK_FLAG	0x10

/** @def TCP_CONNECTED
	@ingroup TCP
	Connected event flag.
	User application can check this flag to determine that
	connection event occurs.
	Can be used to TCP_STATUS->status
*/
#define TCP_CONNECTED	0x01

/** @def TCP_ACKED
	@ingroup TCP
	Ack event flag.
	User application can check this flag to determine that
	client send ack.
	Can be used to TCP_STATUS->status
*/
#define TCP_ACKED		0x02

/** @def TCP_NEWDATA
	@ingroup TCP
	Newdata event flag.
	User application can check this flag to determine that
	client send data.
	Can be used to TCP_STATUS->status
*/
#define TCP_NEWDATA		0x04

/** @def TCP_REXMIT
	@ingroup TCP
	Newdata event flag.
	User application can check this flag to determine that
	after sending some data, no ACK has been received.
	Can be used to TCP_STATUS->status
*/
#define TCP_REXMIT		0x08

/** @def TCP_POOL
	@ingroup TCP
	Pool event flag.
	User application can check this flag to determine that
	TCP stack pool application.
	Can be used to TCP_STATUS->status
*/
#define TCP_POOL		0x10

/** @def TCP_CLOSED
	@ingroup TCP
	Closed event flag.
	User application can check this flag to determine that
	connection is closed.
	Can be used to TCP_STATUS->status
*/
#define TCP_CLOSED		0x20

/** @def TCP_CLOSED
	@ingroup TCP
	Aborted event flag.
	User application can check this flag to determine that
	connection was aborted by client.
	Can be used to TCP_STATUS->status
*/
#define TCP_ABORTED		0x40

/** @def TCP_TIMEDOUT
	@ingroup TCP
	Closed event flag.
	User application can check this flag to determine that
	connection is timed-out.
	Can be used to TCP_STATUS->status
*/
#define TCP_TIMEDOUT	0x80

#define ETHERNET_OFFSET	0x00
#define ARP_OFFSET		0x0E
#define IP_OFFSET		0x0E
#define ICMP_OFFSET		0x22
#define ICMP_DATA		0x2A
#define TCP_OFFSET		0x22
#define UDP_OFFSET		0x22

/** @def TCP_DATA
	@ingroup TCP
	TCP data position at ethBuffer.
*/
#define TCP_DATA		(ETH_ETHERNET_HEADER_LEN+ETH_TCP_HEADER_LEN+ETH_IP_HEADER_LEN)

/** @def UDP_DATA
	@ingroup UDP
	UDP data position at ethBuffer.
*/
#define UDP_DATA		(ETH_ETHERNET_HEADER_LEN+ETH_UDP_HEADER_LEN+ETH_IP_HEADER_LEN)

#define ETH_TYPE_IP			0x0800
#define ETH_TYPE_ARP		0x0806

#define	IP_PROTOCOL_ICMP	0x01
#define	IP_PROTOCOL_TCP		0x06
#define	IP_PROTOCOL_UDP		0x11

#define ICMP_REQUEST		0x08
#define ICMP_REPLY			0x00
#define ETH_ARP_REQUEST		0x0100
#define ETH_ARP_REPLY		0x0200
#define ETH_HW_ETHERNET		0x0001
#define ARP_REPLY_LEN		60
#define ICMP_REPLY_LEN		98
#define ARP_REQUEST_LEN		42
#define MAX_TCP_WINDOW		(MTU_SIZE-100)
#define TCP_TIME_OFF		0xFF
#define ARP_TIME_OFF		0xFF

/** @def HTONS
	@ingroup ETHERNET
	Change byte position in 16bit variable.
	This macro is used to convert little endian to big endian conversely.
	@param n 16bit integer
	@returns 16bit integer
*/
#define HTONS(n)	(uint16_t)((((uint16_t) (n)) << 8) | (((uint16_t) (n)) >> 8))

/** @def HTONS32
	@ingroup ETHERNET
	Change byte position in 32bit variable.
	This macro is used to convert little endian to big endian conversely.
	@param x 32bit integer
	@returns 32bit integer
*/
#define HTONS32(x)	((x & 0xFF000000)>>24)+((x & 0x00FF0000)>>8)+((x & 0x0000FF00)<<8)+((x & 0x000000FF)<<24)

/** @def IP
	@ingroup ETHERNET
	Convert human readable IP presentation to 32bit integer.
	@param a ip address
	@param b ip address
	@param c ip address
	@param d ip address
	@returns 32bit big endian integer
*/
#define IP(a,b,c,d)		((ip_t)(d)<<24)+((ip_t)(c)<<16)+((ip_t)(b)<<8)+a

/** @def PORT
	@ingroup ETHERNET
	Convert human readable PORT number to 16bit big endian integer.
	@param x 16bit integer
	@returns 16bit big endian integer
*/
#define PORT(x)			HTONS(x)

/** @var udp
	@ingroup UDP
	Pointer to udp header.
	@note Valid only for user-application routine!
*/
extern struct ETH_UDP_HEADER *udp;

/** @var tcp
	@ingroup TCP
	Pointer to tcp header.
	@note Valid only for user-application routine!
*/
extern struct ETH_TCP_HEADER *tcp;

/** @var ip
	@ingroup ETHERNET
	Pointer to ip header.
	@note Valid only for user-application routine!
*/
extern struct ETH_IP_HEADER  *ip;

/** @var ethernet
	@ingroup ETHERNET
	Pointer to udp header.
	@note Valid only for user-application routine!
*/
extern struct ETH_ETHERNET_HEADER *ethernet;

/** @var arp
	@ingroup ETHERNET
	Pointer to arp header.
*/
extern struct ETH_ARP_HEADER *arp;

/** @var icmp
	@ingroup ETHERNET
	Pointer to icmp header.
*/
extern struct ETH_ICMP_HEADER *icmp;

/** @var TCP_PORT_TABLE
	@ingroup TCP
	TCP port table.
	@note +1 temporary record
*/
extern struct TCP_PORT_ITEM TCP_PORT_TABLE[MAX_APP_ENTRY+1];

/** @var UDP_PORT_TABLE
	@ingroup UDP
	UDP port table.
*/
extern struct UDP_PORT_ITEM UDP_PORT_TABLE[MAX_APP_ENTRY];

/** @var arpTable
	@ingroup ETHERNET
	ARP table.
*/
extern struct ARP_TABLE arpTable[MAX_ARP_ENTRY];

/** @var tcpTable
	@ingroup TCP
	TCP table.
	@note +1 temporary record
*/
extern struct TCP_TABLE tcpTable[MAX_TCP_ENTRY+1];

/** @var ethBuffer
	@ingroup ETHERNET
	Ethernet buffer. Preserves ethernet packets.
	@note +1 char for '\0' (end of the string)
*/
extern uint8_t ethBuffer[MTU_SIZE + 1];

/** @var eth
	@ingroup ETHERNET
	Used for sending ping.
*/
extern struct ETHERNET eth;

/** @var tcp_conn
	@ingroup TCP
	Preserve pointer to TCP table item.
	@note Valid only for user-application routine!
*/
extern struct TCP_TABLE *tcp_conn;

/** @var tcp_status
	@ingroup TCP
	Preserve events flag.
	@note Valid only for user-application routine!
*/
extern struct TCP_STATUS tcp_status;

extern struct timer_item_s eth_timer;
#ifdef DEBUG_ARPTCP
	extern struct timer_item_s eth_debug_timer;
#endif

/** @ingroup ETHERNET
	Initialization function.
	@note Must be called first!
*/
extern void ethernetInit(void);

/** @ingroup ETHERNET
	Geting the MAC addresses of netgate, ntp and dns.
	@note Should be called at 5min interval.
*/
void ethernetArp(void);

/** @ingroup ETHERNET
	Ethernet main routine.
	Should be called at main routine in never-ending loop.
*/
extern void ETHgetData(void);

/** @ingroup ETHERNET
	Prepare ethernet header at \a ethBuffer.
	@param destination IP address
*/
extern void ETHmakeHeader(ip_t destIp);

/** @ingroup ETHERNET
	Change byte position in 32bit variable.
	This macro is used to convert little endian to big endian conversely.
	@param val 32bit integer
	@returns 32bit integer
*/
extern uint32_t htons32(const uint32_t val);

/** @ingroup ETHERNET
	Change byte position in 16bit variable.
	This macro is used to convert little endian to big endian conversely.
	@param val 16bit integer
	@returns 16bit integer
*/
extern uint16_t htons(const uint16_t val);

/** @ingroup ETHERNET
	Search for MAC address in ARP table.
	@param ip IP address
	@returns index of ARP table
*/
extern uint8_t ARPentrySearch(uint32_t ip);

/** @ingroup ETHERNET
	Request MAC adddress by IP address.
	@param IP address
	@returns 1 for succcess, 0 for fail
	@note If ARP packet was send, you should wait
	some time for response.
*/
extern uint8_t ARPrequest(uint32_t destIp);

extern void ICMPsend(const uint32_t, const uint8_t, const uint16_t, const uint16_t);

/** @ingroup ETHERNET
	Prepare ip header at \a ethBuffer.
	@param destination IP address
*/
extern void IPmakeHeader(const uint32_t destIp);

/** @ingroup UDP
	Send UDP packet
	@param len Size of data to send
	@param scrPort Source port number
	@param dstPort Destination port number
	@param ipAddr IP address
*/
extern void UDPmakeNewPacket(
	const uint16_t len,
	const port_t srcPort,
	const port_t dstPort,
	const ip_t ipAddr
);

/** @ingroup UDP
	UDP open port for listen
	@param port Source port to open
*/
extern void UDPlisten(const port_t port);

/** @ingroup TCP
	Send TCP packet.
	This function depend on data pointed by \a tcp_conn
	pointer. Must be called from stack routine or
	user-application routine ONLY!
	@param len Size of data to send
*/
extern void TCPmakeNewPacket(uint16_t len);

/** @ingroup TCP
	Connect to host.
	@note This function only send connect request (SYN).
	ou must open port for receiving by \a TCPlisten and add
	"Application" for process incoming requests.
	@param destIp Destination IP address
	@param destPort Destination port number
	@returns Store tcp connection record at \a tcp_conn
*/
extern void TCPconnect(const ip_t destIp, const port_t destPort);

/** @ingroup TCP
	Open port for listening.
	@note You should crate function for process incoming request.
	See app_template examples.
	@param port Local port number
*/
extern void TCPlisten(const port_t port);

/** @ingroup TCP
	Delete index stored in \a tcp_conn.
	@param tcp_conn Pointed to tcp table record
*/
extern void TCPindexDel(void);

/** @ingroup TCP
	Send data from user application ONLY!
	@note This functions set flag \a send in tcp table record.
	If ack does not receive, retransmit event will be occur.
	If you want to overlook this event, you should set \a send
	flag to FASLE.
	@param size Size of data to send
*/
extern void TCPsend(const uint16_t size);

/** @ingroup TCP
	Change port of listening to to another.
	@note It is very useful when application must
	reconnect from host. Listening to on the same
	port may produce errors and is highly unrecommended.
	@param port_old Old port number
	@param port_new New port number
*/
extern void TCPappChangePort(
	const port_t port_old,
	const port_t port_new
);

// You should never use this functions at your application code
extern void ETHpacketProcess(void);
extern void ARPentryAdd(void);
extern void ARPreply(void);
extern void ARPtimerDo(void);
extern void ARPprintTable(void);
extern void UDPpacketProcess(void);
extern void TCPprintTable(void);
extern void TCPtimerDo(void);
extern void TCPpacketProcess(void);
extern uint8_t TCPportSearch(const uint16_t);
// You should never use this functions at your application code

#endif /* STACK_H_ */
